/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package izsla.sb;

import izsla.entities.Motorbike;
import izsla.entities.SearchCriteria;
import izsla.entities.Trip;
import izsla.entities.TripUser;
import java.security.MessageDigest;
import java.util.List;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 *
 * @author aki
 */
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class UserManagement implements UserManagementLocal {
    @PersistenceContext
    private EntityManager em;

	/**
	 * SHA-1 hash
	 * @param password the password
	 * @return a 40 char long hex-string value of the hash
	 */
    static private String hashPassword(String password) {
        StringBuilder ret = new StringBuilder(40);
        try {
            MessageDigest md = MessageDigest.getInstance("SHA1");
            byte[] buff = md.digest(password.getBytes());

            for(byte b : buff) {
                int x = b & 0xff;

                if (x < 16) {
                    ret.append('0');
                }
                ret.append(Integer.toHexString(x));
            }
        } catch(Exception e) {
            return null;
        }

        return ret.toString();
    }

	/**
	 * Return true, if the name is not exist in the database. REQ1.
	 * @param name
	 * @return
	 */
    private boolean isNameUnique(String name) {
		Query q = em.createNamedQuery("TripUser.checkName");
		q.setParameter("name", name);
		Long cnt = (Long)q.getSingleResult();
		return cnt == 0;
    }

	/**
	 * Return true, if the e-mail address is not exist in the database. REQ1.
	 * @param email e-mail
	 * @return
	 */
    private boolean isEmailUnique(String email) {
	Query q = em.createNamedQuery("TripUser.checkEmail");
	q.setParameter("email", email);
	Long cnt = (Long)q.getSingleResult();
	return cnt == 0;
    }

    public List<TripUser> getUsersToNotify(Trip trip) {
        Query q = em.createNamedQuery("TripUser.getUsersToNotify");
        q.setParameter("distance", trip.getDistance());
        q.setParameter("start", trip.getStartTime());
        q.setParameter("end", trip.getEndTime());
        return q.getResultList();
    }

	/**
	 * Adds the new user, also calculates the password' hash value.
	 * @param user
	 * @throws AddUserException when REQ1 would be violated.
	 */
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void addUser(TripUser user) throws AddUserException {
		if(!isNameUnique(user.getName())) {
			throw new AddUserException("Username is already taken.");
		}

		if(!isEmailUnique(user.getEmail())) {
			throw new AddUserException("Someone has already registered with this e-mail address.");
		}

		String p = user.getPassword();
		p = hashPassword(p);
		if(p == null) {
			throw new AddUserException("Failed to hash the password.");
		}

		user.setPassword(p);
		em.persist(user);
    }

	/**
	 * Get's the user identified by the name and password.
	 * @param name
	 * @param password
	 * @return the user, or null if it is not found.
	 */
    public TripUser login(String name, String password) {
        Query q = em.createNamedQuery("TripUser.login");
        q.setParameter("name", name);
        q.setParameter("password", hashPassword(password));

        TripUser ret = null;

        try {
            ret = (TripUser)q.getSingleResult();
        }
        catch(NoResultException e) {
        }
        catch(Exception e) {
            System.err.println("Somethin error occured during login.");
        }

        return ret;
    }

    /**
	 * Save the changes of the user.
	 * @param user
	 */
	public void mergeUser(TripUser user) {
		em.merge(user);
    }

    /**
	 * Get a fresh copy of the user, from the persistance context.
	 * @param user
	 * @return
	 */
	public TripUser refreshUser(TripUser user) {
		TripUser ret = em.find(TripUser.class, user.getId());
		em.refresh(ret);
		return ret;
    }

	/**
	 * Removes a motorbike.
	 * @param bike
	 */
    public void deleteBike(Motorbike bike) {
		bike = em.find(Motorbike.class, bike.getId());
		bike.getOwner().getBikes().remove(bike);
		em.remove(bike);
    }

	/**
	 * Delete a search criteria.
	 * @param criteria
	 */
    public void deleteCriteria(SearchCriteria criteria) {
        criteria = em.find(SearchCriteria.class, criteria.getId());
		criteria.getTripUser().getSearchCriterias().remove(criteria);
		em.remove(criteria);
    }

	/**
	 * Change the password of the user.
	 * @param user
	 * @param newPassword
	 */
    public void changePassword(TripUser user, String newPassword) {
		newPassword = hashPassword(newPassword);
		user.setPassword(newPassword);
		em.merge(user);
    }

}
